package org.platform.processor.diagnose.impl;

import com.mc.mic.core.tool.helper.SpringContextHelper;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.platform.constant.DiagnoseConst;
import org.platform.constant.DiagnoseTypeConstant;
import org.platform.dao.mapper.FdDiagnoseTaskTreeNodeMapper;
import org.platform.mo.db.FdDiagnoseTaskTreeNode;
import org.platform.mo.db.FdDiagnosetask;
import org.platform.processor.diagnose.DiagnoseTaskProcessor;
import org.platform.processor.diagnose.IDiagnoseExecutor;
import org.platform.processor.diagnose.tree.IFaultTreeNodeDiagnose;
import org.platform.processor.diagnose.tree.impl.MonitorFaultTreeNodeImpl;
import org.platform.processor.diagnose.tree.impl.TestFaultTreeNodeImpl;
import org.platform.processor.knowledge.TestProcesor;
import org.platform.processor.knowledge.TreeProcessor;
import org.platform.processor.monitor.data.CacheLibraryPO;
import org.platform.tool.util.ComUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 故障树诊断执行
 * <p>
 * 1、树的类型：
 * .   1）描述节点  2）算法节点  3）叶子节点（监测点、测试、故障树）
 * 2、 故障树诊断流程:
 * .   1)监测点叶子节点:
 * .     根据知识id,在报警库中{@link CacheLibraryPO}判断当前是否存在,如果存在,则说明异常反回false,反之返回true
 * .   2)测试叶子节点:
 * .      a.根据知识id查询测试模板表,判断测试模板是否在用?如果没有使用 :如果在用:查询测试通道和测试参数表,跟具测试类型,将其数据添加到 测试通道表以及对应的测试通道参数表
 * .      b.新增任务故障表,设置父诊断ID为当前正在诊断的ID,
 * .      c.由测试任务去发起测试申请,当前诊断根据诊断ID轮询数据库,判断是否成功或者是否超时
 * <p>
 * 3、状态的定义:
 * .    true: 正常
 * .    false: 异常
 *
 * @author
 */
@Data
@Slf4j
@Service
public class FaultTreeDiagnoseImpl implements IDiagnoseExecutor {

    @Autowired
    private DiagnoseTaskProcessor taskProcessor;
    @Autowired
    private TestProcesor testProcesor;
    @Autowired
    private TreeProcessor treeProcessor;

    /**
     * 所有的节点集合map
     */
    private Map<Long, FdDiagnoseTaskTreeNode> treeNodeMap;
    /**
     * 当前诊断列表
     */
    List<FdDiagnoseTaskTreeNode> diagnoseList;

    /**
     * 当前故障树的状态
     */
    private boolean treeStatus;
    private Long knowledgeID;
    private String diagnoseID;

    @Override
    public boolean doExecute(FdDiagnosetask task) {
        this.knowledgeID = task.getKnowledgeid();
        this.diagnoseID = task.getDiagnoseid();
        //查询故障树和对应的故障树节点
        List<FdDiagnoseTaskTreeNode> diagnoseList = treeProcessor.queryFdTreeNodeByDiagnoseId(diagnoseID);
        return diagnosisTree(diagnoseList);
    }

    /**
     * 诊断故障树
     *
     * @param nodeList 节点集合
     * @return 最终故障树的结果
     */
    private boolean diagnosisTree(List<FdDiagnoseTaskTreeNode> nodeList) {
        //将节点信息存放map,方便后面取
        treeNodeMap = nodeList.stream().collect(Collectors.toMap(FdDiagnoseTaskTreeNode::getTreeid, node -> node));


        //根据节点层次分组(默认升序),优先遍历最浅的节点
        Map<String, List<FdDiagnoseTaskTreeNode>> group = nodeList.stream().collect(
                Collectors.groupingBy(node -> StringUtils.substring(node.getPosition(), 0, 1)));

        //根据节点类型排序(默认升序),优先遍历监测点知识
        group.forEach((k, v) -> v.sort(Comparator.comparing(FdDiagnoseTaskTreeNode::getKnowledgetype)));

        diagnoseList = new ArrayList<>();
        group.forEach((k, v) -> v.forEach(list -> {
            if (list.getNodetype() != 2) {
                diagnoseList.add(list);
            }
        }));
        for (Map.Entry<String, List<FdDiagnoseTaskTreeNode>> entry : group.entrySet()) {
            for (FdDiagnoseTaskTreeNode node : entry.getValue()) {
                //这里只诊断叶子节点(判断是否有儿子节点)
                if (isLeafNode(node)) {
                    treeStatus = recursivePNode(node);
                    updateDiagnoseStatus();
                    return treeStatus;
                }
            }
        }
        return treeStatus;
    }

    /**
     * 根据叶子节点遍历整棵树
     * 遍历思路:(向上递归)
     * 1.找到任意叶子节点
     * 2.获取当前叶子节点的状态
     * 3.判断单前叶子节点是否有父节点,
     * .   ①如果没有,则说明当前节点结果是最终结果;
     * .   ②如果有,根据叶子节点状态和父节点的算法类型,判断是否需要取得兄弟节点的状态才能推断单前父节点的最终结果
     * 4.获取兄弟节点的结果思路:(向下递归)
     * 5.判断单前兄弟节点是否有子节点
     * .   ①如果没有,说明当前节点状态就是兄弟节点的最终状态
     * .   ②如果有,遍历所有的子节点,判断当前子节点是否还有子节点
     * .       ①如果没有,根据当前的算法类型返回结果(与:有假则假 或:有真则真)
     * .       ②如果有,则递归当前树,直到能够得到当前子节点结果为止
     * 6.最后返回当前兄弟节点的状态
     *
     * @param node
     * @return
     */
    private boolean recursivePNode(FdDiagnoseTaskTreeNode node) {
        //当前节点的状态
        boolean leafStatus;
        //如果是叶子节点,需要诊断
        if (isLeafNode(node)) {
            leafStatus = doDiagnose(node);
            //如果不是叶子节点,根据当前节点状态判断.   在递归此节点时已经赋值
        } else {
            leafStatus = (node.getStatus() == DiagnoseConst.DIAGNOSE_STATUS_SUCCESS);
        }
        FdDiagnoseTaskTreeNode pNode = treeNodeMap.get(node.getParentid());
        if (null != pNode) {
            List<FdDiagnoseTaskTreeNode> brotherList = getBrotherList(node, pNode);
            for (FdDiagnoseTaskTreeNode brother : brotherList) {
                boolean condition1 = pNode.getArithtype() == 0 && leafStatus;
                boolean condition2 = pNode.getArithtype() == 1 && !leafStatus;
                //根据当前状态和父节点的算法类型,判断是否能够得到结果,如果不能则算兄弟状态(当前是真,条件是与;当前是假,条件是且,都需要推算兄弟)
                if (condition1 || condition2) {
                    boolean brotherStatus = recursiveChildrenNode(brother);
                    leafStatus = algorithm(pNode.getArithtype(), leafStatus, brotherStatus);
                }
            }
            //把当前子节点的状态信息传给爷辈节点,(也可以通过方法中参数传递该状态)
            FdDiagnoseTaskTreeNode ppNode = treeNodeMap.get(pNode.getParentid());
            setStatus(leafStatus, ppNode);
            return recursivePNode(ppNode);
        }
        setStatus(leafStatus, node);
        return leafStatus;
    }

    /**
     * 根据任意节点,判断其状态(结果)
     *
     * @param node 任意及节点
     * @return 节点状态
     */
    private boolean recursiveChildrenNode(FdDiagnoseTaskTreeNode node) {
        boolean thisNodeStatus = false;
        List<FdDiagnoseTaskTreeNode> childrenList = getChildrenList(node);
        //如果当前是叶子节点,则直接返回叶子结果
        if (childrenList.isEmpty()) {
            return doDiagnose(node);
            //如果还有子节点,则根据当前节点算法类型和子节点结果判断当前节点状态
        } else {
            for (FdDiagnoseTaskTreeNode children : childrenList) {
                boolean nodeStatus;
                //判断是叶子节点还是描述节点
                if (isLeafNode(children)) {
                    nodeStatus = doDiagnose(children);
                } else {
                    nodeStatus = recursiveChildrenNode(children);
                    setStatus(nodeStatus, children);
                }
                //处理[与]操作逻辑
                if (0 == children.getArithtype()) {
                    //如果当前是叶子节点,并且结果是假
                    if (nodeStatus) {
                        thisNodeStatus = true;
                    } else {
                        thisNodeStatus = false;
                        break;
                    }
                    //处理[或]操作
                } else {
                    if (nodeStatus) {
                        thisNodeStatus = true;
                        break;
                    } else {
                        thisNodeStatus = false;
                    }
                }
            }
        }
        setStatus(thisNodeStatus, node);
        return thisNodeStatus;
    }

    /**
     * 诊断叶子节点
     *
     * @param node
     * @return
     */
    private boolean doDiagnose(FdDiagnoseTaskTreeNode node) {
        IFaultTreeNodeDiagnose diagnose;
        switch (node.getKnowledgetype()) {
            case DiagnoseTypeConstant.KNOW_MONITOR:
                diagnose = SpringContextHelper.getBean(MonitorFaultTreeNodeImpl.class);
                break;
            case DiagnoseTypeConstant.KNOW_TEST:
                diagnose = SpringContextHelper.getBean(TestFaultTreeNodeImpl.class);
                break;
            default:
                log.error("未找到对应的诊断实例" + node.getKnowledgetype());
                return false;
        }

        return diagnose.doDiagnose(node, diagnoseList);
    }

    /**
     * 判断是否为叶子节点
     *
     * @param node
     * @return
     */
    private boolean isLeafNode(FdDiagnoseTaskTreeNode node) {
        return StringUtils.isEmpty(node.getChildren());
    }

    /**
     * 获取当前节点的所有兄弟节点(不包含自己)
     */
    private List<FdDiagnoseTaskTreeNode> getBrotherList(FdDiagnoseTaskTreeNode thisNode, FdDiagnoseTaskTreeNode pNode) {
        List<FdDiagnoseTaskTreeNode> brotherList = new ArrayList<>();
        String thisNodeId = ComUtils.objectToString(thisNode.getTreeid());
        String children = pNode.getChildren();
        String[] brothers = children.split(",");
        for (String brother : brothers) {
            if (!StringUtils.isEmpty(brother) && !brother.equals(thisNodeId)) {
                brotherList.add(treeNodeMap.get(ComUtils.strToLong(brother)));
            }

        }
        return brotherList;
    }

    /**
     * 获取所有的子节点
     *
     * @param node
     * @return
     */
    private List<FdDiagnoseTaskTreeNode> getChildrenList(FdDiagnoseTaskTreeNode node) {
        String children = node.getChildren();
        String[] split = children.split(",");
        List<FdDiagnoseTaskTreeNode> childrenList = new ArrayList<>();
        for (String s : split) {
            if (!StringUtils.isEmpty(s)) {
                FdDiagnoseTaskTreeNode childrenNode = treeNodeMap.get(Long.parseLong(s));
                //如果子节点是算法节点,就把算法节点的子节点当逻辑节点
                if (childrenNode.getNodetype() == 2) {
                    String children1 = childrenNode.getChildren();
                    String[] split1 = children1.split(",");
                    for (String s1 : split1) {
                        if (!StringUtils.isEmpty(s)) {
                            FdDiagnoseTaskTreeNode childrenNode1 = treeNodeMap.get(Long.parseLong(s1));
                            childrenList.add(childrenNode1);
                        }
                    }
                } else {
                    FdDiagnoseTaskTreeNode childrenNode1 = treeNodeMap.get(Long.parseLong(s));
                    childrenList.add(childrenNode1);
                }
            }
        }
        //这里对儿子节点进行排序,优先遍历子节点最少的
        childrenList.sort(Comparator.comparing(n -> (StringUtils.isEmpty(n.getChildren()) ? 0 : n.getChildren().length())));
        return childrenList;
    }

    /**
     * 根据算法类型,返回两个条件的结果
     *
     * @param type 算法类型 0 与;1 或
     * @param x    条件1
     * @param y    条件2
     * @return 最终结果
     */
    private boolean algorithm(Integer type, boolean x, boolean y) {
        if (type == 0) {
            return x && y;
        } else {
            return x || y;
        }
    }

    /**
     * 最后持久化状态
     */
    private void updateDiagnoseStatus() {
        FdDiagnoseTaskTreeNodeMapper mapper = SpringContextHelper.getBean(FdDiagnoseTaskTreeNodeMapper.class);
        diagnoseList.forEach(diagnose -> mapper.updateByPrimaryKeySelective(diagnose));
    }

    /**
     * 根据诊断结果设置节点状态
     *
     * @param status
     * @param node
     */
    private void setStatus(boolean status, FdDiagnoseTaskTreeNode node) {
        if (status) {
            node.setStatus(DiagnoseConst.DIAGNOSE_STATUS_SUCCESS);
        } else {
            node.setStatus(DiagnoseConst.DIAGNOSE_STATUS_FAILURE);
        }
    }
}
